// Copyright (c) 2007-2020 VMware, Inc. or its affiliates.  All rights reserved.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 2.0 ("MPL"), the GNU General Public License version 2
// ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see
// LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2.  For the ASL,
// please see LICENSE-APACHE2.
//
// This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,
// either express or implied. See the LICENSE file for specific language governing
// rights and limitations of this software.
//
// If you have any questions regarding licensing, please contact us at
// info@rabbitmq.com.


package com.rabbitmq.client.impl;

import java.io.IOException;
import java.util.Date;
import java.util.Map;

import com.rabbitmq.client.LongString;

/**
 * Generates AMQP wire-protocol encoded arguments. Methods on this
 * object are usually called from autogenerated code.
 */
public class MethodArgumentWriter
{
    /** Writes our output */
    private final ValueWriter out;
    /** When encoding one or more bits, records whether a group of bits is waiting to be written */
    private boolean needBitFlush;
    /** The current group of bits */
    private byte bitAccumulator;
    /** The current position within the group of bits */
    private int bitMask;

    /**
     * Constructs a MethodArgumentWriter targeting the given DataOutputStream.
     */
    public MethodArgumentWriter(ValueWriter out)
    {
        this.out = out;
        resetBitAccumulator();
    }

    /** Private API - called to reset the bit group variables. */
    private void resetBitAccumulator() {
        needBitFlush = false;
        bitAccumulator = 0;
        bitMask = 1;
    }

    /**
     * Private API - called when we may be transitioning from encoding
     * a group of bits to encoding a non-bit value.
     */
    private void bitflush()
        throws IOException
    {
        if (needBitFlush) {
            out.writeOctet(bitAccumulator);
            resetBitAccumulator();
        }
    }

    /** Public API - encodes a short string argument. */
    public final void writeShortstr(String str)
        throws IOException
    {
        bitflush();
        out.writeShortstr(str);
    }

    /** Public API - encodes a long string argument from a LongString. */
    public final void writeLongstr(LongString str)
        throws IOException
    {
        bitflush();
        out.writeLongstr(str);
    }

    /** Public API - encodes a long string argument from a String. */
    public final void writeLongstr(String str)
        throws IOException
    {
        bitflush();
        out.writeLongstr(str);
    }

    /** Public API - encodes a short integer argument. */
    public final void writeShort(int s)
        throws IOException
    {
        bitflush();
        out.writeShort(s);
    }

    /** Public API - encodes an integer argument. */
    public final void writeLong(int l)
        throws IOException
    {
        bitflush();
        out.writeLong(l);
    }

    /** Public API - encodes a long integer argument. */
    public final void writeLonglong(long ll)
        throws IOException
    {
        bitflush();
        out.writeLonglong(ll);
    }

    /** Public API - encodes a boolean/bit argument. */
    public final void writeBit(boolean b)
        throws IOException
    {
        if (bitMask > 0x80) {
            bitflush();
        }
        if (b) {
            bitAccumulator |= bitMask;
        } else {
            // um, don't set the bit.
        }
        bitMask = bitMask << 1;
        needBitFlush = true;
    }

    /** Public API - encodes a table argument. */
    public final void writeTable(Map<String, Object> table)
        throws IOException
    {
        bitflush();
        out.writeTable(table);
    }

    /** Public API - encodes an octet argument from an int. */
    public final void writeOctet(int octet)
        throws IOException
    {
        bitflush();
        out.writeOctet(octet);
    }

    /** Public API - encodes an octet argument from a byte. */
    public final void writeOctet(byte octet)
        throws IOException
    {
        bitflush();
        out.writeOctet(octet);
    }

    /** Public API - encodes a timestamp argument. */
    public final void writeTimestamp(Date timestamp)
        throws IOException
    {
        bitflush();
        out.writeTimestamp(timestamp);
    }

    /**
     * Public API - call this to ensure all accumulated argument
     * values are correctly written to the output stream.
     */
    public void flush()
        throws IOException
    {
        bitflush();
        out.flush();
    }
}
